import Color from '../src/utils/Color';
import Point from '../src/utils/Point';
import * as Graph from '../src/utils/Graph/Graph';

let resources = {};

export function updateResources(res) {
    resources = res;
}

// =================== model logic
export function modelCreate(idx) {
    let o = resources['models'][idx];
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        modelUpdateProperty(idx, p);
    }
}
export function modelUpdateProperty(idx, property) {
    let o = resources['models'][idx];
    if (property !== 'shape' && property !== 'size' && property !== 'properties') return;
    // console.warn('[kernel.worker] modelUpdateProperty: ', idx, property, o.vehicleKey);
    (resources['vehicles'] || []).forEach((t, i) => {
        if (t.id.startsWith(o.vehicleKey)) vehicleUpdateProperty(i, 'shape');
    });
}
// =================== contour logic
export function contourCreate(idx) {
    let o = resources['contours'][idx];
    // generateGraph(idx, 'contours');
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        contourUpdateProperty(idx, p);
    }
}
export function contourUpdateProperty(idx, property, type = 'contours') {
    let o = resources[type][idx];
    if (property === 'points') {
        o.iPointsExt = [];
        if (o.points && o.points.length)
            o.points.forEach(p => o.iPointsExt.push(Point.getIsometricPos(p.x, p.y, true)));
    } else if (property === 'color') {
        o.fillExt = o.color || 'rgba(0,0,0,0.25)';
        o.strokeStyleExt = Color.lightenDarkenColor(Color.rgbToHex(o.fillExt), 0.5);
    } else if (property === 'layer') {
        // if (typeof this.contourLayersHidden[o.layer] === 'undefined') this.contourLayersHidden[o.layer] = false;
    } else if (
        property === 'area' ||
        property === 'center' ||
        property === 'label' ||
        property === 'labelColor' ||
        property === 'labelOffset'
    ) {
        o.strokeExt = 'rgba(0, 255, 255, 0.75)';
        // o.strokeExt = Color.lightenDarkenColor(Color.rgbToHex(o.fillExt), 0.5);
        o.strokeWidthExt = Math.pow(o.area * 1000000, 1 / 3);
        updateLabel(type, idx);
    } else if (property === 'properties') {
        generateGraph(idx, type);
    } else {
        // stroke, when selected
        // let idx = this.contours[o.layer].findIndex(v => v.id === o.id),
        //     len = this.contours[o.layer].length;
        // o.strokeExt = 'rgba(0, 255, 255, 0.75)';
        // if (idx < len - 1) {
        //     // adjust array order to resolve the problem of layer coverage
        //     let o = this.contours[o.layer].splice(idx, 1)[0];
        //     this.contours[o.layer].push(o);
        // }
        // if (idx > 0) {
        //     // adjust array order to resolve the problem of layer coverage
        //     let o = this.contours[o.layer].splice(idx, 1)[0];
        //     this.contours[o.layer].unshift(o);
        // }
    }
}
export function generateGraph(idx, type = 'contours') {
    // console.error('generateGrapha:', idx, type, resources[type]);
    let o = resources[type][idx];
    let graphExt = null;
    if (o && o.properties && o.properties.svgShapes) {
        graphExt = Graph.getSimpleObject(o.properties.svgShapes);
    } else if (type === 'contours' || type === 'ranges') {
        // 老数据直接使用points去生成graph对象
        graphExt = {
            tagName: 'polygon',
            points: o.points,
            x: o.points[0].x,
            y: o.points[0].y,
            fill: o.fillExt || o.color || 'rgba(0,0,0,0.25)',
            stroke: 'rgba(0,0,0,0)'
        };
    } else if (type === 'paths') {
        // 老数据直接使用points去生成graph对象
        graphExt = {
            tagName: 'path',
            points: o.controlPoints,
            x: o.controlPoints[0].x,
            y: o.controlPoints[0].y,
            stroke: o.color || 'rgba(0,0,0,0.25)',
            'stroke-width': o.width
        };
    } else if (type === 'vehicles' && o.modelExt && o.modelExt.size) {
        let p = o.modelExt.size;
        let maxLen = Math.max(p.x, p.y);
        let minx = -parseInt(maxLen / 2) + o.tunedPos.x,
            maxx = parseInt(maxLen / 2) + o.tunedPos.x,
            miny = -parseInt(maxLen / 2) + o.tunedPos.y,
            maxy = parseInt(maxLen / 2) + o.tunedPos.y;
        graphExt = {
            tagName: 'polygon',
            points: [
                { x: minx, y: miny },
                { x: maxx, y: miny },
                { x: maxx, y: maxy },
                { x: minx, y: maxy }
            ],
            x: o.tunedPos.x,
            y: o.tunedPos.y,
            fill: o.fillExt || o.color || 'rgba(255,0,0,0.25)',
            stroke: 'rgba(0,0,0,0)'
        };
    }
    let dim = null;
    if (type === 'paths') dim = Point.getDimensions(o.controlPoints);
    else if (type === 'vehicles') dim = Point.getDimensions(graphExt.points);
    else dim = Point.getDimensions(o.points);
    graphExt['dimensions'] = dim;
    let w = dim && dim.length ? Math.abs(dim[1] - dim[0]) : 0;
    let h = dim && dim.length ? Math.abs(dim[3] - dim[2]) : 0;
    graphExt['area'] = (w * h) / 1000000;
    // if (type !== 'paths' && graphExt.tagName === 'path') console.warn('---> ', o.id, type, o.points, o);
    // if (type !== 'paths' && graphExt.tagName === 'path')
    //     graphExt['area'] = Point.getPathStockArea(o.points, graphExt['stroke-width']);
    // if (!graphExt['area'] && type !== 'paths') console.warn('@@@: ', o.id, type, o.points, o, graphExt);
    o.graphExt = graphExt;
}
// =================== range logic
export function rangeCreate(idx) {
    let o = resources['ranges'][idx];
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        rangeUpdateProperty(idx, p);
    }
}
export function rangeUpdateProperty(idx, property) {
    contourUpdateProperty(idx, property, 'ranges');
}

// =================== path logic
export function pathCreate(idx) {
    let o = resources['paths'][idx];
    // console.error('pathCreate', idx, o);
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        pathUpdateProperty(idx, p);
    }
}
export function pathUpdateProperty(idx, property) {
    let o = resources['paths'][idx];
    if (property === 'width') {
        o.pathStrokeWidthExt = o.width;
        o.markerStrokeWidthExt = o.width;
        o.markerStrokeDasharrayExt = o.width;
        o.markerStrokeDashoffsetExt = o.width * 2;

        o.strokeExt = 'rgba(0, 255, 255, 0.75)';
        o.markerDisplayExt = 'inline';
        updateLabel('paths', idx);
        updatePathSigns(idx);
    } else if (property === 'color') {
        o.pathStrokeExt = o.color || 'rgba(0,0,0,0.25)';
    } else if (property === 'label' || property === 'labelColor' || property === 'labelOffset') {
        updateLabel('paths', idx);
    } else if (property === 'controlPoints') {
        let d = getPathDescription(o.controlPoints);
        o.pathDExt = d;
        o.markerDExt = d;
        updateLabel('paths', idx);
        updatePathSigns(idx);

        o.iControlPointsExt = [];
        if (o.controlPoints && o.controlPoints.length)
            o.controlPoints.forEach(p => o.iControlPointsExt.push(Point.getIsometricPos(p.x, p.y, true)));
        o.iPathDExt = o.iMarkerDExt = getPathDescription(o.iControlPointsExt);
    } else if (property === 'bridges') {
        o.iBridgesExt = [];
        if (o.bridges && o.bridges.length)
            o.bridges.forEach(p => o.iBridgesExt.push(Point.getIsometricPos(p.x, p.y, true)));
    } else if (property === 'properties') {
        updatePathSigns(idx);
        generateGraph(idx, 'paths');
    }
}
function getPathDescription(ps = []) {
    let d = 'M' + ps[0].x + ',' + ps[0].y + (ps.length > 2 ? ' C' : ' L');
    for (let i = 1; i < ps.length; i++) d += ps[i].x + ',' + ps[i].y + ' ';
    return d;
}
// const SIGNS = {
//     CLOSURE: 'closure',
//     CONDITION: 'condition',
//     RELATIVESPEED: 'relativeSpeed',
//     STOP: 'stop',
//     SPOTTURN: 'spotTurn',
//     PROHIBITION: 'prohibition',
//     PERMISSION: 'permission'
// };
function updatePathSigns(idx) {
    return;
    let o = resources['paths'][idx];
    if (!resources['pathsSigns']) resources['pathsSigns'] = {};

    let activeTypes = [];
    Object.keys(PATH_SIGNS).forEach(s => {
        if (signIsActive(o, PATH_SIGNS[s])) activeTypes.push(PATH_SIGNS[s]);
    });

    Object.keys(resources['pathsSigns']).forEach(k => {
        if (resources['pathsSigns'][k]['pathIdExt'] === o.id) delete resources['pathsSigns'][k];
    });

    let positions = getSignPositions(o, activeTypes);
    activeTypes.forEach((t, i) => {
        let id = t + 'Sign_' + o.id;
        if (!resources['pathsSigns'][id]) resources['pathsSigns'][id] = {};
        let ext = resources['pathsSigns'][id];
        ext.id = id;
        ext.pathIdExt = o.id;
        ext.typeExt = t;
        ext.widthExt = o.width;
        ext.textExt = getSignText(o, t);
        ext.transformExt = null;
        ext.progressExt = positions[i];
        // if (o.id === '1') console.warn(ext);
    });
}
function signIsActive(o, type) {
    if (type === PATH_SIGNS.CLOSURE) return o.properties[type] && o.properties[type] === 'true';
    if (type === PATH_SIGNS.CONDITION) return o.properties[type] && o.properties[type] !== '100';
    if (type === PATH_SIGNS.RELATIVESPEED) return o.properties[type] && o.properties[type] !== '100';
    if (type === PATH_SIGNS.STOP) return o.properties[type] && o.properties[type] !== '-1';
    if (type === PATH_SIGNS.SPOTTURN) return o.properties[type] && o.properties[type] === 'true';
    if (type === PATH_SIGNS.PROHIBITION) return o.properties[type] && o.properties[type] !== 'NONE';
    if (type === PATH_SIGNS.PERMISSION) return o.properties[type] && o.properties[type] !== 'TCV';
    if (type === PATH_SIGNS.BRIDGE) return o.properties['level'] && o.bridges && o.bridges.length;
}
function getSignText(s, type) {
    if (type === 'closure') return null;
    if (type === 'condition') return s.properties[type];
    if (type === 'relativeSpeed') return s.properties[type];
    if (type === 'stop') return null;
    if (type === 'spotTurn') return null;
    if (type === 'prohibition') return s.properties[type].replace('TCV', '');
    if (type === 'permission') return s.properties[type].replace('TCV', '');
}
function getSignPositions(s, activeTypes) {
    let positions = [],
        start = 0,
        end = 100,
        indexOfStop = activeTypes.indexOf('stop');
    if (indexOfStop >= 0) {
        if (s.properties.stop < 50) start = Number(s.properties.stop);
        else end = Number(s.properties.stop);
    }
    let colCount = activeTypes.length + 1 - (indexOfStop >= 0),
        colWidth = (end - start) / colCount,
        count = 0;
    for (let i = 0; i < activeTypes.length; i++) {
        if (i === indexOfStop) positions[i] = s.properties.stop;
        else positions[i] = start + colWidth * ++count;
    }
    return positions;
}
// =================== vehicles logic
export function vehicleCreate(idx, serverTime, workTime) {
    let o = resources['vehicles'][idx];

    if (!o) return;
    let m = (resources['models'] || []).find(m => new RegExp('^' + m.vehicleKey).test(o.id));
    o.modelExt = m;
    // console.warn('[kernel.worker] vehicleCreate: ', o, m, serverTime, workTime);
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        o = vehicleUpdateProperty(idx, p, serverTime, workTime);
    }
    // ext
    if (!o.shapeExt) o = vehicleUpdateProperty(idx, 'shape', serverTime, workTime);
    return o;
}

export function vehicleUpdateProperty(idx, property, serverTime, workTime) {
    let t = resources['vehicles'][idx];
    // update response time...
    let ms = serverTime - t.lastTelegramTime,
        str = ms + ' ms';
    if (Math.abs(ms) >= 1000) str = Math.floor(ms / 1000) + ' sec';
    if (Math.abs(ms) >= 60000) str = Math.floor(ms / 60000) + ' min';
    if (Math.abs(ms) >= 3600000) str = Math.floor(ms / 3600000) + ' h';
    t.responseExt = str;
    t.jobtimeExt = getTimeString(Math.abs(workTime - t.lastJobTime), true);

    if (property === 'tunedPos') {
        // // 0 to 360 swaps are visible because of css transition
        let angle = t.angleExt || 0;
        angle += normalize(normalize(t.tunedPos.h) - (angle % 360));
        t.angleExt = angle;
        generateGraph(idx, 'vehicles');
    } else if (
        property === 'remoteId' ||
        property === 'isOnline' ||
        property === 'isValidated' ||
        property === 'hasAlert' ||
        property === 'alertColor' ||
        property === 'properties'
    ) {
        let yellow = t.updateIndex >= 0 || t.remoteId !== 'FREE',
            red = !t.isOnline || !t.isValidated || t.properties.stop === 'true';
        // console.log('-----> ', !t.isOnline, !t.isValidated, t.properties.stop === 'true');
        if (yellow || red || t.alertColor.length) {
            // t.blinkerAnimationExt = 'blinker 1s linear infinite';
            t.blinkerFillExt = red
                ? 'rgba(231, 13, 13, 0.8)'
                : t.alertColor.length
                ? t.alertColor
                : 'rgba(227, 216, 15, 0.8)';
        } else {
            t.blinkerFillExt = null;
            t.blinkerAnimationExt = 'none';
        }

        if (t.properties && t.properties.pathId && resources.paths) {
            let lo = resources.paths.find(s => s.id === String(t.properties.pathId));
            t.locationExt = {
                value: lo ? (lo.label ? lo.label : 'ID ' + t.properties.pathId) : 'NONE',
                bg: lo ? lo.labelColor : ''
            };
        }
    } else if (property === 'rawInput') {
        // show or hide parts of the shape according to the status bits
        extendVehicleStatus(t);
    } else if (property === 'envelope') {
        // createEnvelope(...)
        // console.warn('[kernel] ', t.envelope);
    } else if (property === 'route') {
        t.jobInfoExt = 'IDLE';
        t.jobInfoStrokeExt = 'rgba(255,255,255,0.75)';

        if (resources && resources.paths) {
            let deId = t.route ? t.route[t.route.length - 1] : -1,
                de = resources.paths.find(s => s.id === String(deId));
            t.destinationExt = {
                value: de ? (de.label ? de.label : 'ID ' + deId) : 'NONE',
                bg: de ? de.labelColor : ''
            };
        }
    } else if (property === 'shape') {
        let m = t.modelExt;
        if (!m) return;

        t.outlineXExt = String(-m.size.x / 2);
        t.outlineYExt = String(-m.size.y / 2);
        t.outlineWidthExt = m.size.x;
        t.outlineHeightExt = m.size.y;
        t.outlineStrokeWidthExt = String(Math.min(m.size.x, m.size.y) / 20);

        t.shapeExt = m.shape;

        let coronaRadius = isNaN(Number(m.properties.coronaRadius))
            ? Math.max(m.size.x, m.size.y) * 0.75
            : Number(m.properties.coronaRadius);
        t.blinkerRExt = coronaRadius;
        generateGraph(idx, 'vehicles');
    } else if (property === 'visible') {
        /** custom property, show or hiden some vehicles */
        t.visible = 'visibility';
        // ext.visible = 'visibility';
        // t.visible = t.visible;
    }
    return t;
}
function extendVehicleStatus(o) {
    if (!o || !o.modelExt) return;
    let model = o.modelExt,
        status = {};

    if (!model || !model.status) return o;
    for (let k in model.status) {
        if (!model.status.hasOwnProperty(k)) return o;
        let sta = model.status[k];
        if (!sta) return o;

        let val = null;
        if (sta.length > 1) {
            val = sta.index < 0 ? o.properties[k] : Expert.getNumberFromByteArray(o.rawInput, sta.index, sta.length);
        } else {
            val = sta.index < 0 ? o.properties[k] === 'true' : Expert.checkBit(o.rawInput, sta.index);
        }

        let color = val ? sta.color : '';

        if (sta.length > 1) {
            if (sta.color === 'auto') color = val < 50 ? (val < 10 ? 'red' : 'yellow') : 'green';
        }

        status[k] = {
            value: val,
            color
        };
        // console.log('=== ', k, val, color, sta);
    }
    o.statusExt = status;
    return o;
}
// =================== jobs logic
function jobCreate(idx) {
    let o = resources['jobs'][idx];
    if (!o) return;
    for (let p in o) {
        if (!o.hasOwnProperty(p)) continue;
        jobUpdateProperty(idx, p);
    }
}
function jobUpdateProperty(idx, property) {
    let o = resources['jobs'][idx];
    if (property === 'progress') {
        o.progressLabelExt = parseFloat(Math.round(o.progress * 10000) / 100).toFixed(2);
        o.progressColorExt = 'hsla(' + o.progressLabelExt + ', 100%, 50%, 0.5)';
    }
}
// =================== labels logic
function updateLabel(typ, idx) {
    if (!typ || typeof idx === 'undefined' || idx < 0) return;
    let o = resources[typ][idx];
    if (!o || !o.label) return;
    if (!resources[typ + 'Labels']) resources[typ + 'Labels'] = {};
    if (!resources[typ + 'Labels'][o.id]) resources[typ + 'Labels'][o.id] = {};
    let ext = resources[typ + 'Labels'][o.id];
    // if (typ === 'ranges') console.warn(resources);

    if (o.center) {
        // if contours
        ext.x = o.center.x;
        ext.y = o.center.y;
    } else if (o.controlPoints) {
        // if paths
        let cp = o.controlPoints[o.controlPoints.length - 1];
        ext.x = cp.x;
        ext.y = cp.y;
    }
    if (ext.x && ext.y) {
        // ext.transformExt = 'translate(' + o.center.x + ',' + o.center.y + ') rotate(' + (-SVG.getRotation(this.svgMatrix)) + ') scale(1 -1)';
        // ext.transformExt = 'translate(' + ext.x + ',' + ext.y + ') rotate(0) scale(1 -1)';
        // ext.transformExt = `translate(${ext.x} ${ext.y}) rotate(${this.rotation}) scale(1 -1)`;
        ext.transformExt = `translate(${ext.x} ${ext.y}) rotate(0) scale(1 -1)`;
        ext.connectorX1Ext = ext.x;
        ext.connectorY1Ext = ext.y;
    }
    if (o.labelOffset && ext.x && ext.y) {
        ext.connectorDisplayExt = o.labelOffset.x === 0 && o.labelOffset.y === 0 ? 'none' : 'inline';
        ext.x += o.labelOffset.x;
        ext.y += o.labelOffset.y;
        ext.transformExt = `translate(${ext.x} ${ext.y}) rotate(0) scale(1 -1)`;
        ext.connectorX2Ext = ext.x;
        ext.connectorY2Ext = ext.y;
    }
    if (o.label) {
        if (o.area) ext.stockWidthExt = Math.pow(o.area * 1000000, 1 / 3);
        else if (o.width) ext.stockWidthExt = o.width;
        else ext.stockWidthExt = 1;
        ext.lineStrokeWidthExt = ext.stockWidthExt * 4;
        ext.textFontSizeExt = ext.stockWidthExt * 2.25;
        ext.connectorStrokeWidthExt = ext.stockWidthExt / 3;
        ext.stringWidthExt = o.label.length * 520;
        let r = ext.lineStrokeWidthExt / 2,
            half = Math.max(1, ext.stringWidthExt / 2 - r / 2);
        ext.lineX1Ext = -half;
        ext.lineX2Ext = +half;
        // if (o.id === '114') console.warn('@@@ p-sitemap-paths.upateLabel: ', r, half, w);
    }
    if (o.labelColor) {
        ext.lineStrokeExt = o.labelColor;
        ext.connectorStrokeColorExt = o.labelColor;

        // ext.textFillExt = 'rgba(0, 0, 0, 0.75)';
        ext.textFillExt = Color.getContrastColor(o.labelColor);
    }

    ext.textExt = o.label;
    if (ext.textExt) ext.transitionExt = 'all 500ms';
    else ext.transitionExt = 'none';

    let ip = Point.getIsometricPos(ext.x, ext.y, true);
    ext.ix = ip.x;
    ext.iy = ip.y;
}

// =================== Utils
function normalize(angle) {
    if (angle > +180) angle -= 360;
    if (angle < -180) angle += 360;
    return angle;
}
function getTimeString(ms, UTC) {
    var date = new Date(ms);

    var h = UTC ? date.getUTCHours() : date.getHours();
    var m = UTC ? date.getUTCMinutes() : date.getMinutes();
    var s = UTC ? date.getUTCSeconds() : date.getSeconds();

    if (h < 10) h = '0' + h;
    if (m < 10) m = '0' + m;
    if (s < 10) s = '0' + s;

    return h + ':' + m + ':' + s;
}
